﻿using Miao;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

public enum SequencePlayMode
{
    Line,
    Back,
    Circle
}
/// <summary>
/// 帧图播放器。版本v1.1
/// </summary>
public class SequentialPlay
{
    /// <summary>
    /// 保存当前所有已添加2D帧图资源索引
    /// </summary>
    private Dictionary<string, Sprite[]> sequentialDataDict = new Dictionary<string, Sprite[]>();
    public string Name { get; protected set; }
    /// <summary>
    /// 当前使用图集
    /// </summary>
    private SequentialTexture2D sequentialTexture2D;
    protected SpriteRenderer renderer;

    /// <summary>
    /// 完成帧动画一组后回调，可以在回调内加关闭，需要注意除非手动关闭否则帧动画会循环播放。
    /// </summary>
    public event Action OnCompleteLoop;
    /// <summary>
    /// 完成帧动画一组后回调，只会执行一次。
    /// </summary>
    public Action OnComplete;
    /// <summary>
    /// 帧图组改变后回调。
    /// </summary>
    public event Action OnChange;
    /// <summary>
    /// 帧图是否停止
    /// </summary>
    public bool Stop { get; set; }
    /// <summary>
    /// 播放状况保存。
    /// </summary>
    private Action<float> Playing;

    /// <summary>
    /// 链式播放所用副本。
    /// </summary>
    private Queue<Action> ChainComplete = new Queue<Action>();
    /// <summary>
    /// 使用Sprite建立播放表。
    /// </summary>
    /// <param name="Name"></param>
    /// <param name="sprites"></param>
    /// <param name="renderer"></param>
    /// <param name="FirstplayMode"></param>
    public SequentialPlay(string Name, Sprite[] sprites, SpriteRenderer renderer, SequencePlayMode FirstplayMode = SequencePlayMode.Line)
    {

        this.Name = Name;
        sequentialTexture2D = new SequentialTexture2D(sprites);
        sequentialDataDict.Add(Name, sprites);
        this.renderer = renderer;
        ChangeSequencePlayMode(FirstplayMode);
    }

    /// <summary>
    /// 推荐使用Sprite读取。
    /// </summary>
    /// <param name="Name"></param>
    /// <param name="textures"></param>
    /// <param name="renderer"></param>
    /// <param name="FirstplayMode"></param>
    public SequentialPlay(string Name, Texture2D[] textures, SpriteRenderer renderer, SequencePlayMode FirstplayMode = SequencePlayMode.Line)
    {
        Sprite[] sprites = new Sprite[textures.Length];
        for (int a=0;a< textures.Length;a++)
        {
            sprites[a] = textures[a].ToSprite();
        }

        this.Name = Name;
        sequentialTexture2D = new SequentialTexture2D(sprites);
        sequentialDataDict.Add(Name, sprites);
        this.renderer = renderer;
        ChangeSequencePlayMode(FirstplayMode);
    }

    protected float timer = 0;
    protected int serialNumber; //序号
    protected bool IsLastFrame;
    /// <summary>
    /// 链式缓存所需动画，从下一次开始起效。
    /// </summary>
    /// <param name="Name"></param>
    /// <returns></returns>
    public SequentialPlay ChainSequencePlay(string Name)
    {
        ChainComplete.Enqueue(
            () =>
            {
                ChangeSequentialPlay(Name);
            });

        return this;
    }

    public SequentialPlay ChainSequencePlay(string Name, Action OnStart)
    {
        ChainComplete.Enqueue(
            () =>
            {
                OnStart?.Invoke();
                OnStart = null;
                RankSequential(Name);
            });

        return this;
    }

    public SequentialPlay ChainSequencePlay(string Name, Action OnStart, Action OnComplete)
    {
        ChainComplete.Enqueue(
            () =>
            {
                OnStart.Invoke();
                this.OnComplete += OnComplete;
                RankSequential(Name);
            });

        return this;
    }


    /// <summary>
    /// 链式缓存所需动画，从下一次开始起效，播到最后一帧后停止。
    /// </summary>
    /// <param name="Name"></param>
    /// <param name="OnComplete=该链结束时的回调"></param>
    /// <param name="isStop"></param>
    /// <returns></returns>
    public SequentialPlay ChainSequencePlayComplete(string Name,Action OnComplete)
    {
        ChainComplete.Enqueue(
            () =>
            {
                this.OnComplete +=  OnComplete;
                RankSequential(Name);
            });

        return this;
    }


    /// <summary>
    /// 链式缓存所需动画，从下一次开始起效，播放完毕后停在最后一帧。
    /// </summary>
    /// <param name="Name"></param>
    /// <param name="OnComplete"></param>
    /// <returns></returns>
    public SequentialPlay ChainSequenceEnd(string Name, Action OnComplete)
    {
        ChainComplete.Enqueue(
            () =>
            {
                this.OnComplete += OnComplete;
                this.OnComplete += () => Stop = true;
                RankSequential(Name);
            });

        return this;
    }
    /// <summary>
    /// 直接使用Sprites读取比Texture2d更快。
    /// </summary>
    /// <param name="Name"></param>
    /// <param name="sprites"></param>
    public void AddSequential(string Name, Sprite[] sprites)
    {
        this.Name = Name;
        sequentialDataDict.Add(Name, sprites);
    }


    public void AddSequential(string Name, Texture2D[] textures)
    {
        Sprite[] sprites = new Sprite[textures.Length];
        for (int a = 0; a < textures.Length; a++)
        {
            sprites[a] = textures[a].ToSprite();
        }
        this.Name = Name;
        sequentialDataDict.Add(Name, sprites);
    }
    /// <summary>
    /// 改变序列，序列一致的情况下，将序列设为0。链式
    /// </summary>
    /// <param name="Name"></param>
    public SequentialPlay RankSequential(string Name)
    {
        if (this.Name == Name)
        {
            serialNumber = 0;
            return this;
        }
        this.Name = Name;
        sequentialTexture2D.CutSequentialFrame(sequentialDataDict.TryGet(Name));
        serialNumber = 0;//除非额外设定，否则动画切换后从0开始。
        OnChange?.Invoke();
        return this;
    }

    /// <summary>
    /// 改变序列，序列一致的情况下不做操作。
    /// </summary>
    /// <param name="Name"></param>
    public void ChangeSequentialPlay(string Name)
    {
        if (this.Name == Name)
        {
            return;
        }
        this.Name = Name;
        sequentialTexture2D.CutSequentialFrame(sequentialDataDict.TryGet(Name));
        serialNumber = 0;//除非额外设定，否则动画切换后从0开始。
        OnChange?.Invoke();
    }

    /// <summary>
    /// 播放函数，播放形式将以抽象的方式设置。
    /// </summary>
    public void UpadtePlay(float interval)
    {
        Playing?.Invoke(interval);
    }
    /// <summary>
    /// 改变帧动画播放形式。
    /// </summary>
    /// <param name="playMode"></param>
    public void ChangeSequencePlayMode(SequencePlayMode playMode)
    {
        switch (playMode)
        {
            case SequencePlayMode.Line:
                Playing = PlaySequence;
                break;
            case SequencePlayMode.Back:
                Playing = PlaySequenceBack;
                break;
            case SequencePlayMode.Circle:
                Playing = PlaySequenceCircle;
                break;
        }

    }


    /// <summary>
    /// 播放序列，倒着放。
    /// </summary>
    /// <param name="interval=间隔时间"></param>
    protected void PlaySequenceBack(float interval)
    {
        if (Stop) return;
        timer -= Time.deltaTime;
        if (timer < 0)
        {
            //这个在前才会调用OnComplete正常
            if (serialNumber == sequentialTexture2D.playTextures.Length)
            {
                serialNumber = 0;
                OnCompleteLoop?.Invoke();
                if (OnComplete != null)
                {
                    OnComplete.Invoke();
                    OnComplete = null;
                }
                if (ChainComplete.Count != 0)
                {
                    ChainComplete.Dequeue().Invoke();
                }
            }
            renderer.sprite = sequentialTexture2D[sequentialTexture2D.playTextures.Length - serialNumber - 1];
            serialNumber++;
            timer = interval;
        }
    }


    /// <summary>
    /// 播放序列，播放完毕时从0开始从头播放。
    /// </summary>
    /// <param name="interval=间隔时间"></param>
    protected void PlaySequence(float interval)
    {
        if (Stop) return;
        timer -= Time.deltaTime;
        if (timer < 0)
        {
            //这个在前才会调用OnComplete正常
            if (serialNumber == sequentialTexture2D.playTextures.Length)
            {
                serialNumber = 0;
                OnCompleteLoop?.Invoke();
                if (OnComplete != null)
                {
                    OnComplete.Invoke();
                    OnComplete = null;
                }
                if (ChainComplete.Count != 0)
                {
                    ChainComplete.Dequeue().Invoke();
                }
            }
            renderer.sprite = sequentialTexture2D[serialNumber];
            serialNumber++;
            timer = interval;
        }
    }

    private bool Circle;
    /// <summary>
    /// 播放序列，播放完毕时从1开始再倒着播放到0。
    /// </summary>
    /// <param name="interval=间隔时间"></param>
    protected void PlaySequenceCircle(float interval)
    {
        if (Stop) return;
        timer -= Time.deltaTime;
        if (timer < 0)
        {
            //这个在前才会调用OnComplete正常
            if (serialNumber >= sequentialTexture2D.Count)
            {
                serialNumber = sequentialTexture2D.Count-1;
                Circle = false;
                OnCompleteLoop?.Invoke();
                if (OnComplete != null)
                {
                    OnComplete.Invoke();
                    OnComplete = null;
                }
                if (ChainComplete.Count != 0)
                {
                    ChainComplete.Dequeue().Invoke();
                }
            }
            else if (serialNumber < 0)
            {
                serialNumber = 0;
                Circle = true;
                if (ChainComplete.Count != 0)
                {
                    ChainComplete.Dequeue().Invoke();
                }
                OnCompleteLoop?.Invoke();
                if (OnComplete != null)
                {
                    OnComplete.Invoke();
                    OnComplete = null;
                }
            }

            renderer.sprite = sequentialTexture2D[serialNumber];
            serialNumber = Circle ? ++serialNumber : --serialNumber;
            timer = interval;
        }
    }
}

